package Logica.Entidades;

import java.awt.Color;
import java.awt.Graphics;
import java.util.Stack;

import Logica.Mundo;
import Logica.Textures.Sprite;
import Logica.Textures.SpriteManager;


/**
 * Entidad que representa el Robot (Civil) controlado por el Jugador
 * 
 * @author Ignacio del Barrio, Ignacio Jocano, Alexis Aguilera
 */
public class Robot extends Entidad{
	
	/** Orientaciones posibles */
	protected static final int NORTE = 0, ESTE = 1, SUR = 2, OESTE = 3;
	
	/** El Mundo en el que este Robot existe */
	protected Mundo game;
	
	/** Nivel de Nafta del Robot */
	protected int nafta;
	/** Puntaje del Robot */
	protected int puntaje;
	/** Color actual del Pincel */
	protected Color color = Color.WHITE;
	/** Orientacion actual del Robot */
	protected int orientacion;
	/** Conjunto de Coleccionables en el Baul */
	protected Stack<Coleccionable> baul;
	/** Piso donde se encuentra actualmente el Robot */
	protected Piso pisoActual;
	/** Pincel desplegado (V,F)*/
	protected boolean pincel = false;
	/** Icono del Robot en el HUD*/
	protected Sprite hudIcon;
	/** Atributo especial de cada tipo de Robot*/
	protected int accionEspecial;
	/** Verdadero si tiene atributo especial */
	protected boolean tieneAccionEspecial;
	protected boolean moviendose = false;
	
	protected boolean girando = false;
	protected double orientacionGrados;
	
	protected boolean avanzo = false;
	
	/**
	 * Constructor
	 */
	public Robot(Mundo g, Piso actual, String ref, int x,int y, String icon) {
		super(ref, x, y);
		hudIcon = SpriteManager.get().getSprite(icon);
		nafta = 150;
		puntaje = 0;
		orientacionGrados = orientacion = NORTE;		
		baul = new Stack<Coleccionable>();
		this.game = g;
		pisoActual = actual;
		tieneAccionEspecial = false;
		
		//Velocidad de movimiento en ms
		moveSpeed = 250;
	}
	
	/**
	 * Metodos
	 */
	
	public void hazTuGracia() {
		//Reservado para accion especial del Civil
	}
	
	/**
	 * Avanza el Robot al Piso destino. Consume combustible.
	 * No podra avanzar si no tiene combustible, el Piso no es Transitable o hay un objeto en el.
	 * @param destino Destino.
	 * @return Si logro avanzar.
	 */
	public boolean avanzar(Piso origen, Piso destino) {
		if (destino.esTransitable() && destino.getObjeto() == null && nafta > 0){
			if (nafta>=destino.naftaRequerida()){
				destino.setRobot(true);
				origen.setRobot(false);
				setPisoActual(destino);
				avanzo = true;
				//Restar combustible
				decrementarCombustible(origen);
				//Cambio la velocidad en el sentido de movimiento
				switch (orientacion) {
				case NORTE:				
					if (this.y>destino.getY())
						setVerticalMovement(-getMoveSpeed());
					else
						setVerticalMovement(getMoveSpeed());
					break;
				case SUR:
					if (this.y<destino.getY())
						setVerticalMovement(getMoveSpeed());
					else 
						setVerticalMovement(-getMoveSpeed());
					break;
				case ESTE:
					if (this.x<destino.getX())
						setHorizontalMovement(getMoveSpeed());
					else
						setHorizontalMovement(-getMoveSpeed());
					break;
				case OESTE:
					if (this.x>destino.getX())
						setHorizontalMovement(-getMoveSpeed());
					else
						setHorizontalMovement(getMoveSpeed());
					break;
				}
				//Me estoy moviendo
				moviendose = true;
				return true;
			}
		}
		System.out.println("No puedo avanzar!");
		return false;
	}
	
	/**
	 * Determina si el robot se esta moviendo
	 * @return verdadero si el robot se esta moviendo, falso en caso contrario
	 */
	
	public boolean moviendose() {
		return moviendose;
	}
	
	/**
	 * Determina si el robot esta girando
	 * @return verdadero si el robot esta girando, falso en caso contrario
	 */
	
	public boolean girando(){
		return girando;
	}
	
	/**
	 * Mueve el Robot basado en el tiempo transcurrido
	 * 
	 * @param delta El tiempo transcurrido desde la ultima vez que se movio
	 */
	public void move(long delta, int directionMove) {
		//Muevo el Robot
		this.x += (delta * dx) / 1000;
		this.y += (delta * dy) / 1000;
			
		//Si las coordenadas destino son iguales a las coordenadas actuales del Robot
		switch (orientacion) {
		case NORTE:
			if (directionMove==1){
				if (pisoActual.getY() >= this.y) {
					moviendose = false;//Dejo de moverme
					setHorizontalMovement(0);
					setVerticalMovement(0);
				}
			}
			else
				if (pisoActual.getY()<=this.y){
					moviendose = false;//Dejo de moverme
					setHorizontalMovement(0);
					setVerticalMovement(0);
				}
		break;
		case SUR:
			if (directionMove==1){
				if (pisoActual.getY() <= this.y) {
					moviendose = false;//Dejo de moverme
					setHorizontalMovement(0);
					setVerticalMovement(0);
				}
			}
			else
				if (pisoActual.getY() >= this.y) {
					moviendose = false;//Dejo de moverme
					setHorizontalMovement(0);
					setVerticalMovement(0);
				}
		break;
		case ESTE:
			if (directionMove==1){
				if (pisoActual.getX() <= this.x) {
					moviendose = false;//Dejo de moverme
					setHorizontalMovement(0);
					setVerticalMovement(0);
				}
			}
			else
				if (pisoActual.getX() >= this.x) {
					moviendose = false;//Dejo de moverme
					setHorizontalMovement(0);
					setVerticalMovement(0);
				}
		break;
		case OESTE:
			if (directionMove==1){
				if (pisoActual.getX() >= this.x) {
					moviendose = false;//Dejo de moverme
					setHorizontalMovement(0);
					setVerticalMovement(0);
				}
			}
			else
				if (pisoActual.getX() <= this.x) {
					moviendose = false;//Dejo de moverme
					setHorizontalMovement(0);
					setVerticalMovement(0);
				}
		break;
		}
	}
	
	/**
	 * Gira el Robot en una direccion determinada. Consume combustible.
	 * No podra girar si no tiene combustible disponible.
	 * @param dir Direccion de giro. (Izquierda: -1, Derecha: 1)
	 */
	public void girar(int dir) {
		if(nafta > 0){			
			nafta--;
			girando=true;
		}else{
			System.out.println("No puedo girar!");
		}
	}
	
	/**
	 * Animacion que se encarga de girar al robot
	 * @param delta Tiempo transcurrido desde la ultima vez que se movio
	 * @param g graficos sobre el que se efectua el giro
	 * @param dir direccion en la que se gira
	 */
	
	public void moveGirar(long delta, Graphics g, int dir) {		
		switch (dir){
			case -1:
				if (((int)orientacionGrados)==0 && orientacion==NORTE) orientacionGrados=360;
				if (orientacionGrados>((orientacion+OESTE)%4)*90 || (orientacion==ESTE && (orientacionGrados)>=0)){
					orientacionGrados=(orientacionGrados -(delta * getMoveSpeed())/1000);						
					sprite.drawRotated(g,(int) x, (int) y, orientacionGrados);					
				}				
				else{
					orientacion = ((orientacion+OESTE)%4);
					orientacionGrados = orientacion*90;
					girando=false;
				}
				break;
			case 1:
				if ((int)(orientacionGrados)==360 && orientacion==NORTE) orientacionGrados=0;				
				if (orientacionGrados<=((orientacion+1)%4)*90 || (orientacion==OESTE && (orientacionGrados)<=360) ){
					orientacionGrados=(orientacionGrados +(delta * getMoveSpeed())/1000);						
					sprite.drawRotated(g,(int) x, (int) y, orientacionGrados);					
				}	
				else{
					orientacion=((orientacion+1)%4);
					orientacionGrados = orientacion*90;
					girando=false;
				}
				
				
		}
		
	}
	
	/** 
	 * Decrementa el nivel de Nafta actual del Robot
	 * en funcion del terreno en el que se esta moviendo.
	 * 
	 * @param p Piso en el que se mueve
	 */
	protected void decrementarCombustible(Piso p) {
		switch (p.getTipo()) {
			case "arena": 	nafta -= 2;
			break;
			case "asfalto": nafta -= 1;
			break;
		}
	}

	/**
	 * Otorga al Robot un Coleccionable
	 * 
	 * @param col Coleccionable a recibir
	 */
	public void obtenerColeccionable(Coleccionable col) {
		baul.push(col);
	}
	
	/**
	 * El Robot descarga el ultimo Colecionable que obtuvo.
	 * En la celda que tiene enfrente, de ser posible.
	 */
	public void descargarColeccionable(Piso destino) {
		if(!baul.isEmpty()) {
			Coleccionable c = baul.pop();
			c.setX(destino.getX());
			c.setY(destino.getY());
			destino.setObjeto(c);
			c.setPiso(destino);
		}
	}
	
	/**
	 * Actualiza el Color de la pintura del Robot.
	 * 
	 * @param color Codigo Hexadecimal del Color.
	 */
	public void cambiarPintura(Color color) {
		this.color = color;
	}
	
	/**
	 * Despliega el Pincel. Y prepara al Robot para pintar.
	 */	
	public void desplegarPincel() {
		pincel = true;
	}
	
	/**
	 * Despliega el Brazo del Robot. Y agarra lo que tenga enfrente.
	 * De no tener algo enfrente intentara colocar un coleccionable.
	 * De no tener coleccionables no hara nada.
	 * @param destino Piso donde se encuentra el supuesto objeto
	 */	
	public void desplegarBrazo(Piso destino) {
		Objeto obj = destino.getObjeto();
		if(obj != null) {
			obj.dispararEvento(this);
		}else{
			if(destino.getTipo() != "objetivo")
				descargarColeccionable(destino);
		}
	}
	
	/**
	 * Guarda el Pincel. Y desabilita la posibilidad de pintar.
	 */
	public void guardarPincel() {
		pincel = false;
	}
	
	/**
	 * Actualiza el Piso en el que el Robot se encuentra.
	 * 
	 * @param p Piso
	 */
	public void setPisoActual(Piso p) {
		pisoActual = p;
	}
	
	/**
	 * Redefinicion del metodo Draw para poder Dibujar los cambios en el Robot.
	 * 
	 * @param g The graphics context on which to draw
	 */
	public void draw(Graphics g) {
		switch (orientacion) {
			case NORTE: sprite.drawRotated(g,(int) x,(int) y, 0);
				break;
			case SUR: sprite.drawRotated(g,(int) x,(int) y, 180);
				break;
			case ESTE: sprite.drawRotated(g,(int) x,(int) y, 90);
				break;
			case OESTE: sprite.drawRotated(g,(int) x,(int) y,-90);
				break;
		}
	}
	
	/**
	 * Consultas
	 */
	
	/** 
	 * Retorna la cantidad de objetos coleccionables en el baul
	 * 
	 */
	public int getCantColeccionables() {
		return baul.size();
	}
	
	/**
	 * Retorna el nivel de Nafta actual del Robot.
	 * 
	 * @return Cantidad de Nafta.
	 */
	public int getNafta() {
		return nafta;
	}
	
	/**
	 * Retorna el Color actual de la pintura del Robot.
	 * 
	 * @return Codificacion Hexadecimal del Color.
	 */
	public Color getColor() {
		return color;
	}
	
	/**
	 * Retorna el Puntaje actual del Robot.
	 * 
	 * @return Puntaje actual.
	 */
	public int getPuntaje() {
		return puntaje;
	}
	
	/** Retorna el Piso donde se encuentra el Robot
	 *
	 * @return pisoActual Piso actual del Robot
	 */
	public Piso getPisoActual() {
		return pisoActual;
	}
	
	/**
	 * Retorna la direccion en la que apunta el Robot.
	 * 
	 * @return Orientacion. (NORTE: 0, ESTE: 1, SUR: 2, OESTE: 3)
	 */
	public int getOrientacion() {
		return orientacion;
	}
	
	/**
	 * Retorna el icno del HUD del Robot
	 * 
	 * @return El Icono de HUD
	 */
	public Sprite getHUDicon() {
		return hudIcon;
	}
	
	/**
	 * Retorna si tiene accion especial el Robot
	 * 
	 * @return Verdadero si tiene accion especial
	 * 
	 */
	public boolean tieneAccionEspecial() {
		return tieneAccionEspecial;
	}
	
	/**
	 * Retorna el indice de accion especial. Una cantidad.
	 * 
	 * @returns Indice de Accion Especial
	 */	
	public int accionEspecial() {
		return accionEspecial;
	}

	/**
	 * Incrementa el nivel de Nafta del Robot en un valor dado.
	 * 
	 * @param cantidad Cantidad de Combustible.
	 */
	public void addNafta(int i) {
		nafta += i;
	}
	
	/**
	 * Incrementa el Puntaje del Robot en un valor dado.
	 * 
	 * @param cantidad Cantidad de Puntos.
	 */
	public void addPuntaje(int i) {
		puntaje += i;
	}

	/**
	 * Incrementa el nivel de Accion Especial del Robot en un valor dado.
	 * 
	 * @param cantidad Indice de Accion Especial
	 */
	public void addAccionEspecial(int i) {
		accionEspecial += i;
	}
	
	/**
	 * Agrega un Objeto Coleccionable al baul del Robot
	 * 
	 * @param col Objeto Coleccionable
	 */
	public void addColeccionable(Coleccionable col) {
		baul.add(col);
	}
	
	/**
	 * Retorna si el robot es capaz de pintar
	 * 
	 * @param Verdadero si puede pintar
	 */
	public boolean puedePintar() {
		if (pincel && avanzo) {
			avanzo = false;
			return true;
		}
		avanzo = false;
		return false;
	}
	
	/**
	 * Cambia el color del pincel del robot
	 * 
	 * @param color Color nuevo
	 */
	public void setColor(Color color) {
		this.color = color;
		//pincelada.setColor(color);
		//pincelada2.setColor(color);
	}
}
